import argparse
from docx import Document
from docx.shared import Pt, Inches
from docx.oxml.ns import qn
from docx.enum.text import WD_LINE_SPACING, WD_BREAK, WD_ALIGN_PARAGRAPH
from docx.oxml import OxmlElement
import re
import docx
# 自动分页

# 修改段落样式函数
def modify_paragraph_style(paragraph, font_name, chinese_font_name, font_size, line_spacing, line_spacing_rule, first_line_indent_chars):
    if not paragraph.style.name.startswith(('Heading', 'TOC')):
        for run in paragraph.runs:
            run.font.name = font_name  # 设置字体
            run._element.rPr.rFonts.set(qn('w:eastAsia'), chinese_font_name)  # 设置中文字体
            run.font.size = Pt(font_size)  # 设置字号
        paragraph_format = paragraph.paragraph_format
        if line_spacing_rule == 0:
            paragraph_format.line_spacing_rule = WD_LINE_SPACING.MULTIPLE
            paragraph_format.line_spacing = line_spacing  # 设置多倍行距
        else:
            paragraph_format.line_spacing = Pt(line_spacing)  # 设置固定行间距

        # 设置首行缩进，以字符为单位，假设每个字符平均宽度为0.5
        paragraph_format.first_line_indent = Pt(font_size*first_line_indent_chars)

# 修改标题样式函数
def modify_heading_style(paragraph, font_name, chinese_font_name, font_size, line_spacing, line_spacing_rule, first_line_indent_chars, center_heading):
    if paragraph.style.name.startswith(('Heading', 'TOC')):
        for run in paragraph.runs:
            run.font.name = font_name  # 设置标题字体
            run._element.rPr.rFonts.set(qn('w:eastAsia'), chinese_font_name)  # 设置标题中文字体
            run.font.size = Pt(font_size)  # 设置标题字号
        paragraph_format = paragraph.paragraph_format
        if line_spacing_rule == 0:
            paragraph_format.line_spacing_rule = WD_LINE_SPACING.MULTIPLE
            paragraph_format.line_spacing = line_spacing  # 设置标题行间距
        else:
            paragraph_format.line_spacing = Pt(line_spacing)  # 设置标题固定行间距

        # 设置标题首行缩进，以字符为单位，假设每个字符平均宽度为0.25
        # paragraph_format.first_line_indent = Inches(first_line_indent_chars * 0.25)
        paragraph_format.first_line_indent = Pt(font_size * first_line_indent_chars)

        # 如果需要居中对齐标题
        if center_heading:
            paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
        else:
            paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT
            # paragraph_format.first_line_indent = 0
            # paragraph_format.space_after = 0
            # paragraph_format.space_before = 0
            # paragraph_format.hanging_indent = 0
            # paragraph_format.left_indent = 16*4


def apply_styles_to_headings(document):
    headings = [p for p in document.paragraphs if p.style.name.startswith('Heading')]

    for idx, heading in enumerate(headings):
        if idx < 3:
            # 前三个标题设置为方正小标宋简体二号字体
            modify_heading_style(heading, '方正小标宋简体', '方正小标宋简体', 22, 28, 1, 0, True)
        else:
            # 后面的标题设置为黑体三号字体
            modify_heading_style(heading, 'SimHei', '黑体', 16, 28, 1, 0, False)

# 插入分页符函数
def insert_page_break(paragraph):
    # 插入分页符
    paragraph.insert_paragraph_before().add_run().add_break(WD_BREAK.PAGE)

# 检查段落是否为空白
def is_empty_paragraph(paragraph):
    return not paragraph.text.strip()  # 使用strip()确保空白字符被正确检测

# 判断当前段落是否在新页面
def is_paragraph_on_new_page(paragraph):
    # 通过分析段落的底层 XML 判断是否有分页符
    for run in paragraph.runs:
        if 'lastRenderedPageBreak' in run._element.xml or 'w:br' in run._element.xml:
            return True
    return False

# 判断是否已经有分页符
def has_page_break_before(paragraph):
    prev = paragraph._element.getprevious()  # 获取前一个段落的 XML 元素
    if prev is not None:
        # 检查前一个段落的 XML 是否有分页符
        for child in prev:
            if child.tag.endswith('br') and child.get('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}type') == 'page':
                return True
    return False

# 判断段落前面是否有空白回车符
def has_blank_line_before(paragraph):
    prev = paragraph._element.getprevious()  # 获取前一个段落的 XML 元素
    if prev is not None:
        # 检查前一个段落的文本是否为空
        if prev.text is None or prev.text.strip() == '':
            return True
    return False

# 判断段落前面是否有表格
def has_table_before(paragraph):
    prev = paragraph._element.getprevious()  # 获取前一个段落的 XML 元素
    while prev is not None:
        # 检查当前元素是否为表格
        if prev.tag.endswith('tbl'):
            return True
        # 移动到前一个元素
        prev = prev.getprevious()
    return False

def extract_titles_from_docx(doc):
    # 提取标题（Heading）并返回标题及其级别
    titles = []
    for para in doc.paragraphs:
        if para.style.name.startswith('Heading'):
            level = int(para.style.name.split()[-1])  # 获取标题级别
            text = para.text.strip()
            if text:
                titles.append((level, text))
    return titles

def insert_table_of_contents(doc, page_numbers, font_name='宋体', font_size=12, line_spacing=1.5):
    # 查找“目 录”标题的段落
    toc_title_index = -1
    toc_title_pattern = re.compile(r'\b目\s*录\b', re.IGNORECASE)

    for i, paragraph in enumerate(doc.paragraphs):
        if toc_title_pattern.match(paragraph.text.strip()):
            toc_title_index = i
            break

    if toc_title_index == -1:
        raise ValueError("文档中没有找到“目 录”标题")

    # 提取标题及其级别
    toc_content = extract_titles_from_docx(doc)

    # 检查标题数量与页码数量是否匹配
    if len(toc_content) != len(page_numbers):
        raise ValueError("标题数量和页码数量不匹配")

    # 获取“目 录”标题段落的下一个段落位置
    toc_paragraph = doc.paragraphs[toc_title_index]
    next_paragraph_index = toc_title_index + 1

    # 插入每个目录项
    for (level, text), page in zip(toc_content, page_numbers):
        indent = '    ' * (level - 1)  # 根据标题级别设置缩进
        toc_entry = f'{indent}{text} ...... {page}'

        # 在“目 录”标题段落后面插入新段落
        new_paragraph = doc.paragraphs[next_paragraph_index].insert_paragraph_before(toc_entry)

        # 设置字体和格式
        for run in new_paragraph.runs:
            run.font.name = font_name
            run._element.rPr.rFonts.set(qn('w:eastAsia'), font_name)  # 设置中文字体
            run.font.size = Pt(font_size)
        new_paragraph.paragraph_format.line_spacing = line_spacing
        new_paragraph.alignment = docx.enum.text.WD_ALIGN_PARAGRAPH.LEFT  # 设置左对齐

        next_paragraph_index += 1  # 更新插入位置

    # 将“目 录”两个字的对齐方式设置为居中对齐
    toc_paragraph.alignment = docx.enum.text.WD_ALIGN_PARAGRAPH.CENTER


# 修改文档函数
def modify_document(filepath, save_path, skip_paragraphs, font_name, chinese_font_name, font_size, line_spacing, line_spacing_rule,
                    heading_font_name, heading_chinese_font_name, heading_font_size, heading_line_spacing, heading_line_spacing_rule,
                    first_line_indent_chars, heading_first_line_indent_chars, auto_page_break, center_heading, insert_toc):
    # 打开文档
    doc = Document(filepath)
    paragraphs = doc.paragraphs
    i = 0

            # 插入目录
    if insert_toc:
        page_numbers = [1, 2, 6, 7, 8, 9, 12]  # 页码
        insert_table_of_contents(doc,page_numbers,chinese_font_name,font_size,1.5)

    # 跳过指定的段落数
    for _ in range(skip_paragraphs):
        if i < len(paragraphs):
            i += 1

    # 从跳过的段落开始，修改内容并删除空白段落
    for j in range(i, len(paragraphs)):
        paragraph = paragraphs[j]


        # 跳过空白段落
        if is_empty_paragraph(paragraph):
            continue

        # 检查是否是一级标题，并根据条件插入分页符
        if auto_page_break and paragraph.style.name == 'Heading 1':
            if j > 0:
                # 如果前面的段落不是空的，并且标题不是在新页面上，并且没有分页符，才插入分页符
                if not has_blank_line_before(paragraphs[j-1]) and not is_paragraph_on_new_page(paragraph) and not has_page_break_before(paragraph) and has_table_before(paragraph):
                    insert_page_break(paragraph)
                # 如果前面的段落是表格，并且标题不是在新页面上，并且没有分页符，才插入分页符
                elif has_table_before(paragraph) and not is_paragraph_on_new_page(paragraph) and not has_page_break_before(paragraph):
                    insert_page_break(paragraph)

        # 根据段落类型选择修改样式
        if paragraph.style.name.startswith(('Heading', 'TOC')):
            apply_styles_to_headings(doc)
            # modify_heading_style(paragraph, heading_font_name, heading_chinese_font_name, heading_font_size, heading_line_spacing, heading_line_spacing_rule, heading_first_line_indent_chars, center_heading)
        else:
            modify_paragraph_style(paragraph, font_name, chinese_font_name, font_size, line_spacing, line_spacing_rule, first_line_indent_chars)

    # 保存文档到指定路径
    doc.save(save_path)
    return save_path

# 主函数
def main():
    parser = argparse.ArgumentParser(description='Modify the paragraph and heading styles of a Word document, with optional automatic page breaks.')
    parser.add_argument('filepath', type=str, help='Path to the input Word document')
    parser.add_argument('save_path', type=str, help='Path to save the modified Word document')
    parser.add_argument('--skip_paragraphs', type=int, default=22, help='Number of paragraphs to skip (default: 0)')
    parser.add_argument('--insert_toc', action='store_true', help='Insert a table of contents at the beginning of the document')
    # 普通段落参数
    parser.add_argument('--font_name', type=str, default='仿宋_GB2312', help='Font name to apply to paragraphs (default: 仿宋_GB2312)')
    parser.add_argument('--chinese_font_name', type=str, default='仿宋_GB2312', help='Chinese font name to apply to paragraphs (default: 仿宋_GB2312)')
    parser.add_argument('--font_size', type=float, default=16, help='Font size to apply to paragraphs (default: 16)')
    parser.add_argument('--line_spacing', type=float, default=1, help='Line spacing to apply to paragraphs (default: 1)')
    parser.add_argument('--line_spacing_rule', type=int, default=0, help='Line spacing rule to apply to paragraphs (default: 0)')
    parser.add_argument('--first_line_indent_chars', type=int, default=2, help='First line indent for paragraphs in characters (default: 0)')

    # 标题参数
    parser.add_argument('--heading_font_name', type=str, default='方正小标宋简体', help='Font name to apply to headings (default: Arial)')
    parser.add_argument('--heading_chinese_font_name', type=str, default='方正小标宋简体', help='Chinese font name to apply to headings (default: 宋体)')
    parser.add_argument('--heading_font_size', type=float, default=22, help='Font size to apply to headings (default: 22)')
    parser.add_argument('--heading_line_spacing', type=float, default=1.5, help='Line spacing to apply to headings (default: 1.5)')
    parser.add_argument('--heading_line_spacing_rule', type=int, default=0, help='Line spacing rule to apply to headings (default: 0)')
    parser.add_argument('--heading_first_line_indent_chars', type=int, default=0, help='First line indent for headings in characters (default: 0)')

    # 标题居中对齐参数
    parser.add_argument('--center_heading', action='store_true', help='Center align the headings')

    # 自动分页
    parser.add_argument('--auto_page_break', action='store_true', help='Automatically insert page breaks before Heading 1')

    args = parser.parse_args()

    modify_document(
        filepath=args.filepath,
        save_path=args.save_path,
        skip_paragraphs=args.skip_paragraphs,
        font_name=args.font_name,
        chinese_font_name=args.chinese_font_name,
        font_size=args.font_size,
        line_spacing=args.line_spacing,
        line_spacing_rule=args.line_spacing_rule,
        heading_font_name=args.heading_font_name,
        heading_chinese_font_name=args.heading_chinese_font_name,
        heading_font_size=args.heading_font_size,
        heading_line_spacing=args.heading_line_spacing,
        heading_line_spacing_rule=args.heading_line_spacing_rule,
        first_line_indent_chars=args.first_line_indent_chars,
        heading_first_line_indent_chars=args.heading_first_line_indent_chars,
        auto_page_break=args.auto_page_break,
        center_heading=args.center_heading,
        insert_toc=args.insert_toc
    )

if __name__ == '__main__':
    main()
